﻿Imports Microsoft.JScript
Imports Microsoft.JScript.Convert
Imports Microsoft.VisualBasic
Imports System
Imports System.CodeDom.Compiler
Imports System.Collections.Generic
Imports System.Linq
Imports System.Net
Imports System.Reflection
Imports System.Web

Namespace roncliProductions.LibWowArmory

    ''' <summary>
    ''' A class that obtains class talent information from the armory
    ''' </summary>
    ''' <remarks></remarks>
    Public Class Talents

        Private strClass As String
        Private lstTrees As New List(Of TalentTree)

        ''' <summary>
        ''' The class of the talents
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property [Class]() As String
            Get
                Return strClass
            End Get
            Set(ByVal value As String)
                strClass = value
            End Set
        End Property

        ''' <summary>
        ''' The class's talent trees
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property Trees() As List(Of TalentTree)
            Get
                Return lstTrees
            End Get
            Set(ByVal value As List(Of TalentTree))
                lstTrees = value
            End Set
        End Property

        ''' <summary>
        ''' A constructor to create new talents
        ''' </summary>
        ''' <param name="strClass"></param>
        ''' <remarks></remarks>
        Public Sub New(ByVal strClass As String)
            Me.strClass = strClass

            Dim strKey As String = String.Format("LibWowArmory.Talents.{0}", strClass)
            If IsNothing(Cache.Cache(strKey)) Then
                LoadTalents()

                Cache.Cache.Insert(strKey, lstTrees, Nothing, Date.UtcNow.AddDays(1), Nothing, Caching.CacheItemPriority.Normal, Nothing)
            Else
                lstTrees = CType(Cache.Cache(strKey), List(Of TalentTree))
            End If
        End Sub

        ''' <summary>
        ''' Returns the talent that corresponds to the identification number
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function GetTalentByID(ByVal intTalentID As Integer) As Talent
            Return lstTrees.SelectMany(Function(t) t.Talents).Where(Function(t) t.TalentID = intTalentID).First()
        End Function

        ''' <summary>
        ''' Loads the talents from the Armory
        ''' </summary>
        ''' <remarks></remarks>
        Private Sub LoadTalents()
            ' Gather the source code
            Dim lstSource As New List(Of String)
            lstSource.Add("var jsLoaded; var document; var pointsTier = new Array(); var templateString; var reqTalentID; var reqTalentPoints; var variableIsSite; var maxRank; var theUpdatedRank; var showTip; var hideTip; var textNextRank; var textPoint; var textPoints;")
            Using wcClient As New System.Net.WebClient
                lstSource.Add(wcClient.DownloadString("http://www.wowarmory.com/shared/global/talents/includes/variables-live.js"))
                lstSource.Add(wcClient.DownloadString("http://www.wowarmory.com/shared/global/talents/includes/functions.js"))
                lstSource.Add(wcClient.DownloadString(String.Format("http://www.wowarmory.com/shared/global/talents/{0}/data.js", strClass.ToLower.Replace(" ", ""))))
                lstSource.Add(wcClient.DownloadString(String.Format("http://www.wowarmory.com/shared/global/talents/{0}/donotlocalize.js", strClass.ToLower.Replace(" ", ""))))
            End Using

            ' Compile the code
            Dim cdpProvider As CodeDomProvider = CodeDomProvider.CreateProvider("JScript")
            Dim cpParams As New CompilerParameters()
            cpParams.GenerateInMemory = True
            cpParams.GenerateExecutable = True
            Dim crResults As CompilerResults = cdpProvider.CompileAssemblyFromSource(cpParams, lstSource.ToArray())

            ' Run the main module of the assembly
            Dim lstArgs = New List(Of Object)
            lstArgs.Add(Nothing)
            Dim objReturn As Object = crResults.CompiledAssembly.EntryPoint.Invoke(Nothing, BindingFlags.Static, Nothing, lstArgs.ToArray(), Nothing)

            ' Get the type in the assembly that contains the data we want
            Dim tType As Type = (From t In crResults.CompiledAssembly.GetTypes() Where t.Name = "JScript 0").First()

            ' Get the tree names
            Dim fiTalentTrees As FieldInfo = (From f In tType.GetFields() Where f.Name = "tree").First()
            Dim lstTalentTrees As List(Of Object) = CType(ArrayObjectToList(fiTalentTrees.GetValue(Nothing)), List(Of Object))

            ' Get the talents
            Dim fiTalents As FieldInfo = (From f In tType.GetFields() Where f.Name = "talent").First()
            Dim lstTalents As List(Of Object) = CType(ArrayObjectToList(fiTalents.GetValue(Nothing)), List(Of Object))

            ' Get the rank descriptions (some of these are in HTML format)
            Dim fiRanks As FieldInfo = (From f In tType.GetFields() Where f.Name = "rank").First()
            Dim lstRanks As List(Of Object) = CType(ArrayObjectToList(fiRanks.GetValue(Nothing)), List(Of Object))

            ' Create the trees
            For Each objTree As Object In lstTalentTrees
                lstTrees.Add(New TalentTree(CStr(objTree)))
            Next

            ' Create the talents
            For intCount As Integer = 0 To lstTalents.Count - 1
                Dim lstTalent = CType(lstTalents(intCount), List(Of Object))
                Dim tTalent As New Talent(intCount, CStr(lstTalent(1)), 0, CInt(lstTalent(2)), CInt(lstTalent(3)), CInt(lstTalent(4)))
                If lstTalent.Count = 6 Then
                    Dim lstRequiredTalent = CType(lstTalent(5), List(Of Object))
                    tTalent.RequiredTalentID = CInt(lstRequiredTalent(0))
                    tTalent.RequiredTalentPoints = CInt(lstRequiredTalent(1))
                End If

                For Each objRank As Object In CType(lstRanks(intCount), List(Of Object))
                    tTalent.Ranks.Add(CStr(objRank))
                Next

                lstTrees(CInt(lstTalent(0))).Talents.Add(tTalent)
            Next
        End Sub

        ''' <summary>
        ''' A function that unboxes ArrayObject variables to generic lists
        ''' </summary>
        ''' <param name="objObject"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Private Function ArrayObjectToList(ByVal objObject As Object) As Object
            If TypeOf objObject Is ArrayObject Then
                Dim aoObject = CType(objObject, ArrayObject)
                If CInt(aoObject.length) = 0 Then
                    Return New List(Of Object)
                Else
                    Dim arrObject As Array = CType(ToNativeArray(aoObject, Type.GetTypeHandle(New Object)), Array)
                    Dim lstObject As New List(Of Object)
                    For Each obj As Object In arrObject
                        lstObject.Add(ArrayObjectToList(obj))
                    Next
                    Return lstObject
                End If
            Else
                Return objObject
            End If
        End Function

        ''' <summary>
        ''' A class that encapsulates a talent
        ''' </summary>
        ''' <remarks></remarks>
        Public Class Talent

            Private intTalentID As Integer
            Private strName As String
            Private intPoints As Integer
            Private intMaxPoints As Integer
            Private intTreeColumn As Integer
            Private intTreeRow As Integer
            Private intRequiredTalentID As Integer
            Private intRequiredTalentPoints As Integer
            Private lstRanks As New List(Of String)

            ''' <summary>
            ''' The ID number of the talent
            ''' </summary>
            ''' <value></value>
            ''' <returns></returns>
            ''' <remarks></remarks>
            Public Property TalentID() As Integer
                Get
                    Return intTalentID
                End Get
                Set(ByVal value As Integer)
                    intTalentID = value
                End Set
            End Property

            ''' <summary>
            ''' The name of this talent
            ''' </summary>
            ''' <value></value>
            ''' <returns></returns>
            ''' <remarks></remarks>
            Public Property Name() As String
                Get
                    Return strName
                End Get
                Set(ByVal value As String)
                    strName = value
                End Set
            End Property

            ''' <summary>
            ''' The number of points the character has put into this talent
            ''' </summary>
            ''' <value></value>
            ''' <returns></returns>
            ''' <remarks></remarks>
            Public Property Points() As Integer
                Get
                    Return intPoints
                End Get
                Set(ByVal value As Integer)
                    intPoints = value
                End Set
            End Property

            ''' <summary>
            ''' The maximum number of points that can be put into this talent
            ''' </summary>
            ''' <value></value>
            ''' <returns></returns>
            ''' <remarks></remarks>
            Public Property MaxPoints() As Integer
                Get
                    Return intMaxPoints
                End Get
                Set(ByVal value As Integer)
                    intMaxPoints = value
                End Set
            End Property

            ''' <summary>
            ''' The tree's column that the talent appears in
            ''' </summary>
            ''' <value></value>
            ''' <returns></returns>
            ''' <remarks></remarks>
            Public Property TreeColumn() As Integer
                Get
                    Return intTreeColumn
                End Get
                Set(ByVal value As Integer)
                    intTreeColumn = value
                End Set
            End Property

            ''' <summary>
            ''' The tree's row that the talent appears in
            ''' </summary>
            ''' <value></value>
            ''' <returns></returns>
            ''' <remarks></remarks>
            Public Property TreeRow() As Integer
                Get
                    Return intTreeRow
                End Get
                Set(ByVal value As Integer)
                    intTreeRow = value
                End Set
            End Property

            ''' <summary>
            ''' The ID of the talent that is required to take this talent
            ''' </summary>
            ''' <value></value>
            ''' <returns></returns>
            ''' <remarks></remarks>
            Public Property RequiredTalentID() As Integer
                Get
                    Return intRequiredTalentID
                End Get
                Set(ByVal value As Integer)
                    intRequiredTalentID = value
                End Set
            End Property

            ''' <summary>
            ''' The number of points required to be in the required talent to take this talent
            ''' </summary>
            ''' <value></value>
            ''' <returns></returns>
            ''' <remarks></remarks>
            Public Property RequiredTalentPoints() As Integer
                Get
                    Return intRequiredTalentPoints
                End Get
                Set(ByVal value As Integer)
                    intRequiredTalentPoints = value
                End Set
            End Property

            ''' <summary>
            ''' The HTML text that describes each rank's abilities
            ''' </summary>
            ''' <value></value>
            ''' <returns></returns>
            ''' <remarks></remarks>
            Public Property Ranks() As List(Of String)
                Get
                    Return lstRanks
                End Get
                Set(ByVal value As List(Of String))
                    lstRanks = value
                End Set
            End Property

            ''' <summary>
            ''' A constructor to create new talent information
            ''' </summary>
            ''' <param name="intTalentID"></param>
            ''' <param name="strName"></param>
            ''' <param name="intPoints"></param>
            ''' <param name="intMaxPoints"></param>
            ''' <param name="intTreeColumn"></param>
            ''' <param name="intTreeRow"></param>
            ''' <remarks></remarks>
            Public Sub New(ByVal intTalentID As Integer, ByVal strName As String, ByVal intPoints As Integer, ByVal intMaxPoints As Integer, ByVal intTreeColumn As Integer, ByVal intTreeRow As Integer)
                Me.intTalentID = intTalentID
                Me.strName = strName
                Me.intPoints = intPoints
                Me.intMaxPoints = intMaxPoints
                Me.intTreeColumn = intTreeColumn
                Me.intTreeRow = intTreeRow
            End Sub

        End Class

        ''' <summary>
        ''' A class that encapsulates a talent tree
        ''' </summary>
        ''' <remarks></remarks>
        Public Class TalentTree

            Private strName As String
            Private lstTalents As New List(Of Talent)

            ''' <summary>
            ''' The name of the talent tree
            ''' </summary>
            ''' <value></value>
            ''' <returns></returns>
            ''' <remarks></remarks>
            Public Property Name() As String
                Get
                    Return strName
                End Get
                Set(ByVal value As String)
                    strName = value
                End Set
            End Property

            ''' <summary>
            ''' The tree's talents
            ''' </summary>
            ''' <value></value>
            ''' <returns></returns>
            ''' <remarks></remarks>
            Public Property Talents() As List(Of Talent)
                Get
                    Return lstTalents
                End Get
                Set(ByVal value As List(Of Talent))
                    lstTalents = value
                End Set
            End Property

            ''' <summary>
            ''' A constructor to create a new talent tree
            ''' </summary>
            ''' <param name="strName"></param>
            ''' <remarks></remarks>
            Public Sub New(ByVal strName As String)
                Me.strName = strName
            End Sub

        End Class

    End Class

End Namespace